using AppLevelSecurityProvider.Models;
using GrapeCity.Forguncy.SecurityProvider;
using TenantSecurityProvider.Models;
using TenantSecurityProvider.Utils;

namespace TenantSecurityProvider;

/// <summary>
/// 一个简单的基于 多租户 实现的安全提供程序样例
/// </summary>
public class AppLevelSecurityProvider : ISecurityProvider, ISupportSettings, IVerifyUserAsync
{
    /// <summary>
    /// 应用ID
    /// </summary>
    private const string SETTINGS_NAME_APPID = "该应用的标识";

    /// <summary>
    /// 应用ID的自定义属性名称
    /// </summary>
    private const string SETTINGS_NAME_APPIDCUSTOMIZEDPROPERTYNAME = "存放应用标识的自定义属性名称";

    /// <summary>
    /// 组织根节点名称 属性名称
    /// </summary>
    private const string SETTINGS_NAME_ORGANIZATIONROOTNODEIDPROPERTYNAME = "该应用的组织结构根节点";

    /// <summary>
    /// 上一次获取的用户信息, 在该程序内部中，请勿直接读取UserInformations属性，而应该使用此字段，因为直接读取UserInformations属性，会发送请求，会出现性能问题
    /// </summary>
    private UserInformations _lastUserInformations = new();

    /// <summary>
    /// 安全提供程序名称，主要用于在服务器中展示
    /// </summary>
    public string Name => "应用级隔离安全提供程序";

    /// <summary>
    /// 第三方系统中的所有用户信息，如果不需要提供用户信息，则返回 null
    /// </summary>
    public UserInformations UserInformations
    {
        get
        {
            var config = ConfigUtils.GetConfig();

            if (string.IsNullOrWhiteSpace(config.AppId))
            {
                return new UserInformations();
            }

            // 所有的角色信息
            var roles = UserServiceRequestUtils.GetAllRolesAsync().Result;
            // 所有的用户信息
            var userInfos = UserServiceRequestUtils.GetAllUsersAsync().Result;
            // 所有的组织节点信息
            var organizationNodeInfos = UserServiceRequestUtils.GetAllOrganizationNodeInfosAsync().Result;
            // 用于存储组织节点信息，key为父节点ID，value为子节点信息列表
            var organizationNodeInfosDic =
                organizationNodeInfos.GroupBy(i => i.ParentID).ToDictionary(i => i.Key, i => i.ToList());
            // 用于存储组织成员信息，key为组织节点ID，value为组织成员信息列表
            var organizationMemberDic = UserServiceRequestUtils.GetOrganizationMemberDicAsync().Result;

            // 用于存储角色信息，key为角色名称，value为角色对象
            var roleDic = new Dictionary<string, Role>();
            // 用于存储用户信息，key为用户ID，value为用户对象
            var userDic = new Dictionary<string, User>();
            // 最终返回的用户信息
            var userInformations = new UserInformations();

            // 将角色信息转换为角色对象，并添加到用户信息中
            foreach (var keyValuePair in roles)
            {
                var role = new Role
                {
                    Name = keyValuePair.Value,
                };
                userInformations.Roles.Add(role);
                roleDic[keyValuePair.Value] = role;
            }

            // 将用户信息转换为用户对象，并添加到用户信息中
            foreach (var user in userInfos)
            {
                var item = user.CustomProperties.FirstOrDefault(i => i.PropertyName == config.AppIdCustomizedPropertyName);

                if (item?.Value != config.AppId)
                {
                    continue;
                }

                var securityProviderUser = new User
                {
                    UserId = user.UserName,
                    FullName = user.FullName,
                    Email = user.Email,
                };

                user.CustomProperties.ForEach(i => { securityProviderUser.Properties.Add(i.PropertyName, i.Value); });
                user.RoleNames.ForEach(i =>
                {
                    roleDic.TryGetValue(i, out var role);
                    role?.Users.Add(securityProviderUser);
                });

                userInformations.Users.Add(securityProviderUser);
                userDic[user.UserName] = securityProviderUser;
            }

            // 组织根节点信息
            var rootOrganizationNodeInfo =
                organizationNodeInfos.FirstOrDefault(i => i.ParentID == 0 && i.Name == config.OrganizationRootNodeName);

            if (rootOrganizationNodeInfo != null)
            {
                var organization = new OrganizationExt()
                {
                    Name = rootOrganizationNodeInfo.Name,
                    ID = rootOrganizationNodeInfo.ID,
                };

                if (organizationMemberDic.TryGetValue(rootOrganizationNodeInfo.ID, out var organizationMemberInfos))
                {
                    foreach (var organizationMemberInfo in organizationMemberInfos)
                    {
                        if (!userDic.TryGetValue(organizationMemberInfo.UserName, out var user))
                        {
                            continue;
                        }

                        var organizationRoles = new List<Role>();

                        foreach (var organizationRoleId in organizationMemberInfo.OrganizationRoles)
                        {
                            if (roles.TryGetValue(organizationRoleId.ToString(), out var roleName))
                            {
                                if (roleDic.TryGetValue(roleName, out var role))
                                {
                                    organizationRoles.Add(role);
                                }
                            }
                        }

                        var organizationMember = new OrganizationMember()
                        {
                            User = user,
                            IsLeader = organizationMemberInfo.IsLeader,
                            OrganizationRoles = organizationRoles
                        };

                        organization.Members.Add(organizationMember);
                    }
                }

                void FillSubOrganization(Organization parentOrganization,
                    OrganizationNodeInfo parentOrganizationNodeInfo)
                {
                    if (organizationNodeInfosDic.TryGetValue(parentOrganizationNodeInfo.ID,
                            out var subOrganizationNodeInfos))
                    {
                        foreach (var subOrganizationNodeInfo in subOrganizationNodeInfos)
                        {
                            var subOrganization = new OrganizationExt()
                            {
                                Name = subOrganizationNodeInfo.Name,
                                ID = subOrganizationNodeInfo.ID,
                            };


                            if (organizationMemberDic.TryGetValue(subOrganization.ID, out var organizationMemberInfos))
                            {
                                foreach (var organizationMemberInfo in organizationMemberInfos)
                                {
                                    if (!userDic.TryGetValue(organizationMemberInfo.UserName, out var user))
                                    {
                                        continue;
                                    }

                                    var organizationRoles = new List<Role>();

                                    foreach (var organizationRoleId in organizationMemberInfo.OrganizationRoles)
                                    {
                                        if (roles.TryGetValue(organizationRoleId.ToString(), out var roleName))
                                        {
                                            if (roleDic.TryGetValue(roleName, out var role))
                                            {
                                                organizationRoles.Add(role);
                                            }
                                        }
                                    }

                                    var organizationMember = new OrganizationMember()
                                    {
                                        User = user,
                                        IsLeader = organizationMemberInfo.IsLeader,
                                        OrganizationRoles = organizationRoles
                                    };

                                    subOrganization.Members.Add(organizationMember);
                                }
                            }

                            parentOrganization.SubOrganizations.Add((Organization)subOrganization);
                            FillSubOrganization((Organization)subOrganization, subOrganizationNodeInfo);
                        }
                    }
                }

                FillSubOrganization(organization, rootOrganizationNodeInfo);
                userInformations.Organizations.Add(organization);
            }

            _lastUserInformations = userInformations;
            return userInformations;
        }
    }

    /// <summary>
    /// 用户认证的类型，目前默认为 UserNameAndPassword 即账号密码认证
    /// 此例中也以这种方式进行认证
    /// </summary>
    public AuthenticationType AuthenticationType => AuthenticationType.UserNameAndPassword;

    /// <summary>
    /// 第三方应用获取的数据如何在活字格中保存，此例中使用内存缓存
    /// </summary>
    public UserInformationStorageMode UserInformationStorageMode => UserInformationStorageMode.InMemoryCache;

    /// <summary>
    /// 是否支持登出操作，如果支持，则在活字格中可以执行登出操作
    /// </summary>
    public bool AllowLogout => true;

    /// <summary>
    /// 更新设置
    /// </summary>
    /// <param name="dictionary"></param>
    public void UpdateSetting(Dictionary<string, object> dictionary)
    {
        var config = ConfigUtils.GetConfig(true);

        if (dictionary.TryGetValue(SETTINGS_NAME_APPIDCUSTOMIZEDPROPERTYNAME, out var appIdName))
        {
            var value = appIdName?.ToString();
            config.AppIdCustomizedPropertyName = value;
        }

        if (dictionary.TryGetValue(SETTINGS_NAME_APPID, out var appId))
        {
            var value = appId?.ToString();
            config.AppId = value;
        }

        if (dictionary.TryGetValue(SETTINGS_NAME_ORGANIZATIONROOTNODEIDPROPERTYNAME, out var organizationRootName))
        {
            var value = organizationRootName?.ToString();
            config.OrganizationRootNodeName = value;
        }

        ConfigUtils.SetConfig(config);
    }

    /// <summary>
    /// 设置项
    /// </summary>
    public List<SecurityProviderSettings> Settings
    {
        get
        {
            var config = ConfigUtils.GetConfig(true);
            var organizationNodeInfos = UserServiceRequestUtils.GetAllOrganizationNodeInfosAsync().Result;

            var orgs = organizationNodeInfos
                    .Where(i => i.ParentID == 0)
                    .Select(i => i.Name)
                    .ToList();
            orgs.Insert(0, "请选择组织结构根节点...");

            var comboEditor = new ComboEditor()
            {
                Items = orgs
            };

            var settings = new List<SecurityProviderSettings>
            {
                new()
                {
                    Name = SETTINGS_NAME_APPIDCUSTOMIZEDPROPERTYNAME,
                    Value = config.AppIdCustomizedPropertyName,
                },
                new()
                {
                    Name = SETTINGS_NAME_APPID,
                    Value = config.AppId,
                },
                new()
                {
                    Name = SETTINGS_NAME_ORGANIZATIONROOTNODEIDPROPERTYNAME,
                    Editor = comboEditor,
                    Value = config.OrganizationRootNodeName,
                }
            };

            return settings;
        }
    }

    /// <summary>
    /// 认证用户，在登录活字格时会调用此方法, 此实例中使用的是VerifyUserAsync，所以此方法不会被调用
    /// </summary>
    public User VerifyUser(Dictionary<string, string> properties)
    {
        return null;
    }

    /// <summary>
    /// 认证用户，在登录活字格时会调用此方法
    /// </summary>
    public async Task<User> VerifyUserAsync(Dictionary<string, string> properties)
    {
        var user = _lastUserInformations.Users.FirstOrDefault(i => i.UserId == properties["userName"]);

        // 如果用户不存在，则直接返回 null
        if (user == null)
        {
            return null;
        }

        // 如果用户存在，但是租户ID不匹配，则直接返回 null
        if (user.Properties.TryGetValue(SETTINGS_NAME_APPIDCUSTOMIZEDPROPERTYNAME, out var value))
        {
            var config = ConfigUtils.GetConfig();
            if (value.ToString() != config.AppId)
            {
                return null;
            }
        }

        var result = await UserServiceRequestUtils.ValidateUser(properties["userName"], properties["password"]);

        var userInfo = await UserServiceRequestUtils.GetUserInfoAsync(properties["userName"]);

        var newUser = new User
        {
            UserId = userInfo.UserName,
            FullName = userInfo.FullName,
            Email = userInfo.Email,
            Properties =
            {
                ["FGC_Role"] = userInfo.Role != null ? string.Join(",", userInfo.Role) : null
            }
        };

        return result ? newUser : null;
    }
}
